package com.pnnd.stream;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;

public class RandomAccessHeap extends AbstractMemorySegment {

    protected int offset;
    protected int pageCnt;

    RandomAccessHeap(int pageCnt, Object owner) {
        super(new byte[(int)(pageCnt*MEMORY_PAGE_SIZE)], owner);
        this.offset = 0;
        this.pageCnt = pageCnt;
    }

    @Override
    public ByteBuffer wrap(int offset, int length) {

        return ByteBuffer.wrap(this.heapMemory,offset,length);
    }

    @Override
    public byte get(int index) {
        return this.heapMemory[index];
    }

    @Override
    public void put(int index, byte b) {
        this.heapMemory[index] = b;
    }

    @Override
    public void get(int index, byte[] dst) {

        if(dst == null){
            throw new NullPointerException();
        }

        if(this.offset < index){
            throw new IndexOutOfBoundsException();
        }

        System.arraycopy(this.heapMemory,index,dst,0, Math.max(dst.length,this.offset - index));
    }

    @Override
    public void put(int index, byte[] src) {
        if(src == null){
            throw new NullPointerException();
        }

        if(this.heapMemory.length < index +src.length || index > this.heapMemory.length-1){
            throw new IndexOutOfBoundsException();
        }

        System.arraycopy(src,0,this.heapMemory,Math.min(index,this.offset),src.length);
    }

    @Override
    public void get(int index, byte[] dst, int offset, int length) {

        if(dst == null){
            throw new NullPointerException();
        }

        if(dst.length < offset+length){
            throw new IndexOutOfBoundsException("dst");
        }

        if(this.heapMemory.length < index +dst.length || index > this.heapMemory.length-1){
            throw new IndexOutOfBoundsException();
        }

        System.arraycopy(this.heapMemory,index,dst,offset,length);
    }

    @Override
    public void put(int index, byte[] src, int offset, int length) {
        if(src == null){
            throw new NullPointerException();
        }

        if(src.length < offset+length){
            throw new IndexOutOfBoundsException("dst");
        }

        if(this.heapMemory.length < index +src.length || index > this.heapMemory.length-1){
            throw new IndexOutOfBoundsException();
        }

        System.arraycopy(src,offset,this.heapMemory,index,length);
    }

    @Override
    public boolean getBoolean(int index) {
        return this.heapMemory[index] != 0;
    }

    @Override
    public void putBoolean(int index, boolean value) {
        this.heapMemory[index] = (byte) (value ? 1 : 0);
    }

    @Override
    public void get(DataOutput out, int offset, int length) throws IOException {
        if(this.heapMemory.length < offset+length){
            throw new IndexOutOfBoundsException();
        }

        out.write(this.heapMemory,offset,length);
    }

    @Override
    public void put(DataInput in, int offset, int length) throws IOException {
        if(in == null){
            throw new NullPointerException();
        }

        if(offset < this.address || offset+length > this.addressLimit){
            throw new IndexOutOfBoundsException();
        }

        in.readFully(this.heapMemory,offset,length);
    }

    @Override
    public void get(int offset, ByteBuffer target, int numBytes) {
        if(target == null){
            throw new NullPointerException();
        }

        if(offset < this.address || offset+numBytes > this.addressLimit){
            throw new IndexOutOfBoundsException();
        }

        target.put(this.heapMemory,offset,numBytes);
    }

    @Override
    public void put(int offset, ByteBuffer source, int numBytes) {
        if(source == null){
            throw new NullPointerException();
        }

        if(offset < this.address || offset+numBytes > this.addressLimit){
            throw new IndexOutOfBoundsException();
        }

        source.get(this.heapMemory,offset,numBytes);
    }

    @Override
    public long end() {
        return this.addressLimit;
    }

    @Override
    public long start() {
        return this.address;
    }

    @Override
    public int physicsPageNum() {
        return this.pageCnt;
    }

    @Override
    public long offset() {
        throw new NotImplementedException();
    }

    @Override
    public boolean hasMore() {
        throw new NotImplementedException();
        //return this.offset < this.addressLimit;
    }

    @Override
    public boolean canWrote(int size) {
        throw new NotImplementedException();
        //return this.offset + size < this.addressLimit;
    }
}
